use crate::extension::index::index::query::index_attribute::IndexAttribute;
use crate::extension::index::index::query::index_spec::IndexSpec;
use halo_model::{ExtensionGVK, ExtensionOperator, GVK};
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};

pub struct IndexDescriptor<T>
where
    T: ExtensionGVK,
{
    pub spec: IndexSpec<T>,
    // Record whether the index is ready, managed by {@code IndexBuilder}.
    pub ready: bool,
}

impl<T> IndexDescriptor<T>
where
    T: ExtensionGVK,
{
    pub fn identity(&self) -> String {
        format!("{}{}", self.spec.name.clone(), self.ready)
    }
    pub fn new(spec: IndexSpec<T>) -> Self {
        IndexDescriptor { spec, ready: false }
    }
}

impl<T> Hash for IndexDescriptor<T>
where
    T: ExtensionGVK,
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.spec.hash(state);
        self.ready.hash(state)
    }
}

impl<T> PartialEq for IndexDescriptor<T>
where
    T: ExtensionGVK,
{
    fn eq(&self, other: &Self) -> bool {
        self.spec.eq(&other.spec) && self.ready == other.ready
    }
}
impl<T> Eq for IndexDescriptor<T> where T: ExtensionGVK {}

impl<T> Debug for IndexDescriptor<T>
where
    T: ExtensionGVK,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "IndexDescriptor {{ spec: {}, ready: {} }}",
            self.spec.name, self.ready
        )
    }
}

#[cfg(test)]
mod tests {
    use crate::extension::index::index::query::index_attribute::IndexAttributeFactory;
    use crate::extension::index::index::query::index_descriptor::IndexDescriptor;
    use crate::extension::index::index::query::index_spec::{IndexSpec, OrderType};
    use crate::tests::index_view_data_set::FakeExtension;
    use halo_model::ExtensionOperator;

    #[test]
    fn equals_verifier() {
        let spec1 = IndexSpec::new(
            "metadata.name".to_string(),
            OrderType::ASC,
            true,
            IndexAttributeFactory::simple_attribute(
                "FakeExtension".to_string(),
                |e: &FakeExtension| e.get_metadata().get_name(),
            ),
        );
        let spec2 = IndexSpec::new(
            "metadata.name".to_string(),
            OrderType::ASC,
            true,
            IndexAttributeFactory::simple_attribute(
                "FakeExtension".to_string(),
                |e: &FakeExtension| e.get_metadata().get_name(),
            ),
        );

        let descriptor = IndexDescriptor::new(spec1);
        let descriptor2 = IndexDescriptor::new(spec2);
        assert_eq!(descriptor, descriptor2);

        let spec3 = IndexSpec::new(
            "metadata.name".to_string(),
            OrderType::DESC,
            false,
            IndexAttributeFactory::simple_attribute(
                "FakeExtension".to_string(),
                |e: &FakeExtension| e.get_metadata().get_name(),
            ),
        );
        let descriptor3 = IndexDescriptor::new(spec3);
        assert_eq!(descriptor, descriptor3);
    }
}
